﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using Microsoft.Crm.Sdk;
using Microsoft.Crm.Sdk.Query;

namespace AutoNumberPlugin
{
    /*
     * <Settings>
     * <setting name='autoNumberAttributeName'>
     * <value>new_campaignnumber</value>
     * </setting>
     * </Settings>
     */

    /// <summary>
    /// This plugin is intended to hook into create message of new_marketingcampaign entity.
    /// This plugin will generate the next auto-numbering for the specified entity.
    /// </summary>
    public class AutoNumbering : IPlugin
    {
        string _autoNumberEntityName = "new_autonumber";
        string _autoNumberEntityEntityName = "new_entityname";
        string _autoNumberEntityAttributeName = "new_attributename";
        string _autoNumberEntityNextAutoNumberName = "new_nextautonumber";

        string _configAttributeName = "autoNumberAttributeName";

        string autoNumberedEntityName = string.Empty;
        string autoNumberedAttributeName = string.Empty;

        /// <summary>
        /// Default constructor with config.
        /// </summary>
        /// <param name="unsecureConfig"></param>
        /// <param name="secureConfig"></param>
        public AutoNumbering(string unsecureConfig, string secureConfig)
        {
            PluginConfiguration secureConfigHelper = new PluginConfiguration(secureConfig);

            autoNumberedAttributeName = secureConfigHelper.GetValueNode(_configAttributeName);

            if (string.IsNullOrEmpty(autoNumberedAttributeName))
            {
                PluginConfiguration unsecureConfigHelper = new PluginConfiguration(unsecureConfig);

                autoNumberedAttributeName = secureConfigHelper.GetValueNode(_configAttributeName);

                if (string.IsNullOrEmpty(autoNumberedAttributeName))
                {
                    throw new InvalidPluginExecutionException("No autoNumberAttributeName is specified in the plugin config.");
                }
            }
        }

        /// <summary>
        /// Main entry point of the plugin.
        /// </summary>
        /// <param name="context">The plugin execution context.</param>
        public void Execute(IPluginExecutionContext context)
        {
            if (context == null) throw new InvalidPluginExecutionException("Context is null.", new ArgumentNullException("context"));

            // Verify that there is an entity in the target
            if (context.InputParameters.Properties.Contains(ParameterName.Target) && context.InputParameters.Properties[ParameterName.Target] is DynamicEntity)
            {
                // Obtain the target business entity from the input parmameters.
                DynamicEntity entity = (DynamicEntity)context.InputParameters[ParameterName.Target];

                autoNumberedEntityName = entity.Name;

                string messageUpperCase = context.MessageName.ToUpperInvariant();
                if (messageUpperCase == "CREATE")
                {
                    int nextAutoNumber;
                    CrmServiceWrapper service = new CrmServiceWrapper(context);

                    nextAutoNumber = GetNextAutoNumber(service);

                    entity.Properties[autoNumberedAttributeName] = new CrmNumber(nextAutoNumber);
                }
            }
        }

        /// <summary>
        /// Get the next auto number
        /// </summary>
        /// <param name="service"></param>
        /// <returns></returns>
        private int GetNextAutoNumber(CrmServiceWrapper service)
        {
            int nextAutoNumber;

            // Search for lastAutonumber for the entity
            Collection<ConditionExpression> expressions = new Collection<ConditionExpression>();
            ConditionExpression entityNameExpression = new ConditionExpression();
            entityNameExpression.AttributeName = _autoNumberEntityEntityName;
            entityNameExpression.Operator = ConditionOperator.Equal;
            entityNameExpression.Values = new object[] { autoNumberedEntityName };
            expressions.Add(entityNameExpression);

            ConditionExpression attributeNameExpression = new ConditionExpression();
            attributeNameExpression.AttributeName = _autoNumberEntityAttributeName;
            attributeNameExpression.Operator = ConditionOperator.Equal;
            attributeNameExpression.Values = new object[] { autoNumberedAttributeName };
            expressions.Add(attributeNameExpression);

            Collection<DynamicEntity> results = service.RetrieveMultipleDynamicEntityWithMultipleCondition(_autoNumberEntityName,
                                                                       expressions,
                                                                       new AllColumns());
            DynamicEntity result;
            if (results == null || results.Count == 0)
            {
                nextAutoNumber = 1;

                // Create new auto number record for this entity starting with 1
                result = new DynamicEntity();
                result.Name = _autoNumberEntityName;
                result.Properties[_autoNumberEntityEntityName] = autoNumberedEntityName;
                result.Properties[_autoNumberEntityAttributeName] = autoNumberedAttributeName;
                result.Properties[_autoNumberEntityNextAutoNumberName] = new CrmNumber(2);

                service.Create(result);
            }
            else if (results.Count == 1)
            {
                result = results[0];

                nextAutoNumber = ((CrmNumber)result.Properties[_autoNumberEntityNextAutoNumberName]).Value;

                result.Properties[_autoNumberEntityNextAutoNumberName] = new CrmNumber(nextAutoNumber + 1);

                service.Update(result);
            }
            else
                throw new InvalidPluginExecutionException("Duplicate records exist.");
            return nextAutoNumber;
        }
    }
}
